package com.heima.wemedia.service.impl;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;


import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.constants.wemedia.WemediaContans;
import com.heima.common.exception.CustomException;
import com.heima.common.message.NewsAutoScanConstants;
import com.heima.common.message.WmNewsMessageConstants;
import com.heima.model.admin.dtos.NewsAuthDto;
import com.heima.model.common.dtos.PageResponseResult;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.media.dtos.WmNewsDto;
import com.heima.model.media.dtos.WmNewsPageReqDto;
import com.heima.model.media.pojos.WmMaterial;
import com.heima.model.media.pojos.WmNews;
import com.heima.model.media.pojos.WmNewsMaterial;
import com.heima.model.media.pojos.WmUser;
import com.heima.model.media.vo.WmNewsVo;
import com.heima.utils.threadlocal.WmThreadLocalUtils;

import com.heima.wemedia.controller.v1.WmMaterialController;
import com.heima.wemedia.mapper.WmMaterialMapper;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.mapper.WmNewsMaterialMapper;
import com.heima.wemedia.service.WmNewsService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Transactional
@Slf4j
public class WmNewsServoceImpl extends ServiceImpl<WmNewsMapper, WmNews> implements WmNewsService {

    @Value("${fdfs.url}")
    String fileServerUrl;
    private WmNews wmNews;

    @Override
    public ResponseResult findList(WmNewsPageReqDto dto) {
        //检查参数
        if (dto==null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        dto.checkParam();
        //检查是否登录
        WmUser user = WmThreadLocalUtils.getUser();
        if (user==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        // 封装条件
        //分页条件
        Page<WmNews> pageRequest = new Page<>(dto.getPage(), dto.getSize());
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = Wrappers.<WmNews>lambdaQuery();
        // 检查状态条件
        if (dto.getStatus() !=null){
            lambdaQueryWrapper.eq(WmNews::getStatus,dto.getStatus());
        }
        //检查关键字条件，模糊查询

        if (StringUtils.isNotBlank(dto.getKeyword())){
            lambdaQueryWrapper.like(WmNews::getTitle,dto.getKeyword());
        }

        //检查频道条件
        if(dto.getChannelId() !=null){
            lambdaQueryWrapper.eq(WmNews::getChannelId,dto.getChannelId());
        }
        //检查发布日期 时间条件
        if (dto.getBeginPubDate() !=null){
            lambdaQueryWrapper.eq(WmNews::getPublishTime,dto.getBeginPubDate());
        }
        if (dto.getEndPubDate() !=null){
            lambdaQueryWrapper.eq(WmNews::getPublishTime,dto.getEndPubDate());
        }
        // 检查登录用户ID条件
        lambdaQueryWrapper.eq(WmNews::getUserId,user.getId());
        //按照文章的发布时间来排序
        lambdaQueryWrapper.orderByDesc(WmNews::getPublishTime);
        // 执行查询 封装结果
        IPage<WmNews> pageResult = page(pageRequest, lambdaQueryWrapper);
        PageResponseResult responseResult = new PageResponseResult(dto.getPage(), dto.getSize(), pageRequest.getTotal());
        responseResult.setHost(fileServerUrl);
        responseResult.setData(pageResult.getRecords());
        return responseResult;
    }

    @Autowired
    KafkaTemplate<String,String> kafkaTemplate;
    @Transactional
    @Override
    public ResponseResult summitNews(WmNewsDto dto) {
        //检查并封装参数
        //检查文章内容 状态 用户是否登录
        if(dto==null && StringUtils.isEmpty(dto.getContent()) ){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        if (!checkNewsStatus(dto.getStatus())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章方法错误");
        }
        WmUser wmUser = WmThreadLocalUtils.getUser();
        if (wmUser ==null){
            return  ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
        }
        //将dto的内容转化为wmNews实体对象
        WmNews wmNews = new WmNews();
        //将dto的属性copy到wmNews中（同名称，同属性）
        BeanUtils.copyProperties(dto,wmNews);

        // 如果dto中的type为自动(-1)的话， 需要暂时将type设置null,生成封面后载赋值
        if (WemediaContans.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            wmNews.setType(null);
        }
        //将dto中的images图片集合转化为字符串，多个图片用逗号拼接
        //[http://ip/图片路径，xxxx，---]
        List<String> coverImages = dto.getImages();//封面图片列表
        if (coverImages!=null && !coverImages.isEmpty()){
             coverImages = coverImages.stream()
                    .map(imgUrl -> imgUrl.replace(fileServerUrl, "").trim())//把fileServerUrl替换空串，trim 去除空格 空
                    .collect(Collectors.toList());//搜集到新的集合
            //将集合中的每一个元素都用逗号隔开的方式拼接起来
            String join = StringUtils.join(coverImages, ",");
            //设置成封面字符串
            // ===> 图片路径1 ，图片路径2 ，图片路径3，xxxx,---
            wmNews.setImages(join);
        }

        //保存或者修改文章（根据ID是否存在）
        wmNews.setUserId(wmUser.getId());
        saveOrUpdateWmNews(wmNews);

        //保存文章中内容涉及的素材 和 封面中涉及的素材 他们的关联关系
        //首先抽取内容中部分涉及的图片路径url  封装到集合
     List<String> imageList= ectractUrlInfo(dto.getContent());
     //如果不是草稿 设置抽取图片与素材关联关系
        //状态不能为草稿   抽取出来的imageList不为空 要有值
        if(!WmNews.Status.NORMAL.getCode().equals(dto.getStatus())&& imageList!=null&&!imageList.isEmpty()){
            //保存内容和素材的关系                                     内容引用
            saveRelativeInfo(imageList,wmNews.getId(),WemediaContans.WM_CONTENT_REFERENCE);

        }
        //如果不是草稿，设置封面图片与素材的关联关系
        //如果dto=type为自动类型 需要自动生成封面图片
        if (imageList!=null && !imageList.isEmpty()&&WemediaContans.WM_NEWS_TYPE_AUTO.equals(dto.getType())){
            coverImages=autoSetCoverImages(wmNews,imageList);
        }
        //封面 1 内容 0
        if (!WmNews.Status.NORMAL.getCode().equals(dto.getStatus())&&coverImages!=null&&!coverImages.isEmpty()){
            saveRelativeInfo(coverImages,wmNews.getId(),WemediaContans.WM_COVER_REFERENCE);
        }
        if(wmNews.getStatus()==1){
            kafkaTemplate.send(NewsAutoScanConstants.WM_NEWS_AUTO_SCAN_TOPIC, JSON.toJSONString(wmNews.getId()));
        }

        return ResponseResult.okResult();
    }

    /**
     * 根据内容中的图片列表自动生成封面图片列表
     * @param wmNews
     * @param imageList  内容中的图片列表
     * @return
     */
    private List<String> autoSetCoverImages(WmNews wmNews, List<String> imageList) {
        int size = imageList.size();//内容图片数量
        List<String> coverImages =new ArrayList<>();
        if (size>2){
            //1 图片的数量是否大于2 将type类型设置为多图 截图图片列表中的前 3 张图片作为封面图片
            coverImages=imageList.stream().limit(3).collect(Collectors.toList());
            wmNews.setType(WemediaContans.WM_NEWS_MANY_IMAGE);
        }else if (size>0 && size<=2){
            //2 0<图片的数量<=2 将type类型设置为单图 截图图片列表中的前 1 张图片作为封面图片
            //skip() 忽略前几条
            coverImages=imageList.stream().limit(1).collect(Collectors.toList());
            wmNews.setType(WemediaContans.WM_NEWS_SINGLE_IMAGE);
        }else {
            //3 图片的数量为0 将type类型设置为无图
            wmNews.setType(WemediaContans.WM_NEWS_NONE_IMAGE);
        }

        if (!coverImages.isEmpty()){
            wmNews.setImages(StringUtils.join(coverImages,","));
        }
        updateById(wmNews);//因为更改type类型，所以要修改文章
        //返回封面图片集合     用于设置关联关系
        return coverImages;
    }
     @Autowired
     private WmMaterialMapper wmMaterialMapper;
    /**
     * 保存 文章图片和素材的关联关系
     * @param imageList 图片列表
     * @param newsId  文章Id
     * @param type 引用类型 0 为内容  1 为封面
     */
    private void saveRelativeInfo(List<String> imageList, Integer newsId, Short type) {
      //根据 用户ID  imageList（url） 查询关联素材 判断in（url，url，url）  素材
        List<WmMaterial> wmMaterials = wmMaterialMapper.selectList(
                Wrappers.<WmMaterial>lambdaQuery()
                        .in(WmMaterial::getUrl, imageList)
                        .eq(WmMaterial::getUserId, WmThreadLocalUtils.getUser().getId())
        );
        if (wmMaterials==null || wmMaterials.isEmpty()){
            throw new CustomException(ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"关联素材不存在"));
        }
        //将素材的信息转化成为map集合 List<Wmmaterial>=> url作为key id作为value封装成一个map集合
        Map<String, Integer> urlAndIdMap = wmMaterials.stream().collect(Collectors.toMap(WmMaterial::getUrl, WmMaterial::getId));
        //判断所有图片在素材中是否存在（遍历图片列表，判断每一张图片map集合中是否存在 如果不存在 抛异常 如果存在  取出素材ID）
        List<Integer> materialIdList=new ArrayList<>();
        for (int i = 0; i <imageList.size() ; i++) {
            String currentImg = imageList.get(i);
            if (!urlAndIdMap.containsKey(currentImg)){
                    throw  new CustomException(ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"关联元素不存在"));
            }
            materialIdList.add(urlAndIdMap.get(currentImg));
        }
        //根据素材ID 文章ID 引用类型  将关联数据保存到数据库 (批量插入，for循环效率低)
        wmNewsMaterialMapper.saveRelationsByContent(materialIdList,newsId,type);//批量保存关联关系


    }

    /**
     * 抽取文章内容中的图片路径
     * @param content 文章内容 string= "[{type: text ,value:"文本内容“}，{type：image, value:"图片路径"}]"
     * @return
     */
    private List<String> ectractUrlInfo(String content) {
    //把json字符串转换成集合
        List<Map> mapList= JSONArray.parseArray(content,Map.class);
        //创建新的集合收集图片
        List<String> imageList= new ArrayList<>();
        //遍历集合拿到type为image的value
        for (Map map : mapList) {
            String type = (String) map.get("type");
            if (WemediaContans.WM_NEWS_TYPE_IMAGE.equals(type)){
                String imgUrl =(String) map.get("value");
                imageList.add(imgUrl.replace(fileServerUrl,"").trim());//去除fileServerUrl前缀
            }
        }

        return imageList;

    }

    @Autowired
    private WmNewsMaterialMapper wmNewsMaterialMapper;

    /**
     * 保存或者修改文章（根据ID是否存在）
     * @param wmNews
     */
    private void saveOrUpdateWmNews(WmNews wmNews) {

        wmNews.setCreatedTime(new Date());
        wmNews.setSubmitedTime(new Date());
        wmNews.setEnable((short)1);//上架

        //如果id为空  新增文章
        if(wmNews.getId()==null){
            save(wmNews);
        }else {
            //如果id不为空代表修改
            //删除该文章与素材的关系
            wmNewsMaterialMapper.delete(
                    Wrappers.<WmNewsMaterial>lambdaQuery()
                            .eq(WmNewsMaterial::getNewsId,wmNews.getId())//关于文章的都删除
            );
            updateById(wmNews);
        }



    }

    /**
     * 文章检查状态 不能为null 必须是0或者是 1
     * @param status
     * @return
     */
    private boolean checkNewsStatus(Short status) {
        if(status !=null && (WmNews.Status.NORMAL.getCode().equals(status))||(WmNews.Status.SUBMIT.getCode().equals(status))){
            return true;
        }
        return false;
    }


    @Override
    public ResponseResult findWmNewsById(Integer id) {
        //检查参数
        if(id==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //根据ID查找数据
        WmNews wmNews = getById(id);
        if(wmNews==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章数据不存在");
        }

        //结果 返回
        ResponseResult responseResult = ResponseResult.okResult(wmNews);
        responseResult.setHost(fileServerUrl);

        return responseResult;
    }

    @Override
    public ResponseResult delNews(Integer id) {
        //检查参数
        if(id==null){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //根据文章id  当前登录用户ID 查询文章数据(不能删除其他用户的文章数据)
        WmNews news = getOne(
                Wrappers.<WmNews>lambdaQuery()
                        .eq(WmNews::getId,id)//id条件
                        .eq(WmNews::getUserId,WmThreadLocalUtils.getUser().getId())
        );
        if(news == null){
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //如果status为发布 并且 enable为上架 不能删除
        if(WmNews.Status.PUBLISHED.equals(news.getStatus())&&news.getEnable()==1){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"文章已上架，不能删除");
        }
        //删除文章和素材的关联关系
        wmNewsMaterialMapper.delete(
                Wrappers.<WmNewsMaterial>lambdaQuery()
                        .eq(WmNewsMaterial::getNewsId,id)
        );
        //删除文章数据
        removeById(id);
        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult downOrUp(WmNewsDto dto) {
        //检查参数    enable (0(下架)   1(上架))
        if(dto == null || (dto.getEnable()!=0 && dto.getEnable()!=1) || dto.getId() == null
        ){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        //获取文章 ( status == 9 只有发布的文章才可以上下架)
        WmNews wmNews = getOne(Wrappers.<WmNews>lambdaQuery()
                .eq(WmNews::getId, dto.getId())
                .eq(WmNews::getUserId, WmThreadLocalUtils.getUser().getId())
        );
        if (wmNews == null) {
            return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        if (!WmNews.Status.PUBLISHED.getCode().equals(wmNews.getStatus())) {
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"只有发布状态的文章才可以上下架");
        }
        //修改文章
//        update(
//                Wrappers.<WmNews>lambdaUpdate()
//                        .set(WmNews::getEnable,dto.getEnable())
//                        .eq(WmNews::getId,dto.getId())
//        );
        boolean flag = update(
                Wrappers.<WmNews>lambdaUpdate()
                        .set(WmNews::getEnable,dto.getEnable())
                        .eq(WmNews::getId,dto.getId())
        );
        if(flag&&wmNews.getArticleId()!=null){
            //  articleId     enable
            Map<String,Object> map = new HashMap<>();
            map.put("articleId",wmNews.getArticleId());
            map.put("enable",dto.getEnable());
            kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(map));
        }
        //返回响应
        return ResponseResult.okResult();

    }

    /**
     * 查询状态为 4 或 8 并且 发布时间小于等于当前时间
     * @return
     */
    @Override
    public List<Integer> findRelease() {

        // 构建查询条件
        LambdaQueryWrapper<WmNews> lambdaQueryWrapper = Wrappers.<WmNews>lambdaQuery();
        // 查询 发布时间小于等于当前时间 并且 (状态 为4人工审核 或 8审核通过)
        // where publish_time <= NOW() and  (`status` = 4 or `status` = 8)
        lambdaQueryWrapper.le(WmNews::getPublishTime,new Date()).and(w -> w.eq(WmNews::getStatus,4).or().eq(WmNews::getStatus,8));
        log.info("查询待发布文章的sql条件语句====> {}",lambdaQueryWrapper.getSqlSegment());
        //执行查询
        List<WmNews> list = list(lambdaQueryWrapper);
        //获取新闻Id列表
        List<Integer> newsIdList = list.stream().map(WmNews::getId).collect(Collectors.toList());
        return newsIdList;
    }

    @Autowired
    private WmNewsMapper wmNewsMapper;

    @Override
    public PageResponseResult findList(NewsAuthDto dto) {
        // dto 分页参数   title
        dto.checkParam();
        Integer page = dto.getPage();
        Integer size = dto.getSize();
        dto.setPage((page-1)*size);
        // 补充title参数
        String title = dto.getTitle();
        if(StringUtils.isNotBlank(title)){
            dto.setTitle("%"+title+"%");
        }
        // 得到wmnews的分页数据
        List<WmNewsVo> listAndPage = wmNewsMapper.findListAndPage(dto);
        long listCount = wmNewsMapper.findListCount(dto);
        // 封装结果并返回
        PageResponseResult responseResult = new PageResponseResult(page,size,listCount);
        responseResult.setData(listAndPage);
        return responseResult;
    }
    @Override
    public WmNewsVo findWmNewsVo(Integer id) {
        return wmNewsMapper.findWmNewsVo(id);
    }
}
